use std::{
    fs,
    sync::{Arc, LazyLock},
};

use arc_swap::ArcSwapOption;
use log::debug;
use openssl::ssl::SslContext;
use serde::{Deserialize, Serialize};

use super::{
    core_conf::{HttpCoreConf, HttpCoreRuntimeConf},
    h2_conf::{H2ServerConf, H2ServerRuntimeConf},
    proxy_conf::{HttpProxyConf, HttpProxyRuntimeConf},
    site_mgr::get_ssl_acceptor_conf,
};

use crate::{
    config::ssl::{
        acceptor::{SslAcceptorConf, SslAcceptorRuntimeConf},
        connector::{SslConnectorConf, SslConnectorRuntimeConf},
    },
    error::*,
    openssl::acceptor::CustomAcceptorBuilder,
    utils::file::{file_name, is_dir},
};

#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(default)]
pub struct HttpMainConf {
    pub sites_enabled_path: String,
    // default 1024
    pub server_conn_pool_capcity: Option<usize>,
    // default 4
    pub client_conn_pool_nums: Option<usize>,
    // default 1024
    pub client_conn_pool_capacity: Option<usize>,
    // default 16
    pub client_conn_pool_one_pre_host: Option<usize>,
    pub ssl_acceptor_conf: Option<SslAcceptorConf>,
    pub h2_server_conf: Option<H2ServerConf>,
    pub ssl_connector_conf: Option<SslConnectorConf>,
    pub core_conf: Option<HttpCoreConf>,
    pub proxy_conf: Option<HttpProxyConf>,
}

#[allow(dead_code)]
impl HttpMainConf {
    pub fn load_from_yaml(path: &str) -> Result<HttpMainRuntimeConf> {
        let conf_str = fs::read_to_string(path).if_err(
            ErrorType::ParseError,
            format!("Unable to read conf file from {path}"),
        )?;
        debug!("HttpMainConf file read from {}", file_name(path));
        Self::from_yaml(&conf_str)
    }

    pub fn from_yaml(conf_str: &str) -> Result<HttpMainRuntimeConf> {
        let conf: HttpMainConf = serde_yaml::from_str(conf_str)
            .if_err(ErrorType::ParseError, "Unable to parse linker conf")?;
        conf.to_runtime_conf()
    }

    pub fn to_runtime_conf(&self) -> Result<HttpMainRuntimeConf> {
        let sites_enabled_path = self.sites_enabled_path.clone();
        if sites_enabled_path.is_empty()
            || !sites_enabled_path.starts_with('/')
            || !is_dir(&sites_enabled_path)
        {
            return Error::e_explain(
                ErrorType::InvalidError,
                "http sites_enabled path must be an absolute dir path",
            );
        }

        let server_conn_pool_capcity =
            self.server_conn_pool_capcity.unwrap_or(1024);
        let client_conn_pool_nums = self.client_conn_pool_nums.unwrap_or(4);
        let client_conn_pool_capacity =
            self.client_conn_pool_capacity.unwrap_or(1024);
        let client_conn_pool_one_pre_host =
            self.client_conn_pool_one_pre_host.unwrap_or(16);

        let ssl_acceptor_conf = match &self.ssl_acceptor_conf {
            Some(conf) => conf,
            None => &SslAcceptorConf::default(),
        };
        let ssl_acceptor_runtime_conf =
            ssl_acceptor_conf.to_runtime_conf(None)?;

        let h2_server_conf = match &self.h2_server_conf {
            Some(conf) => conf,
            None => &H2ServerConf::default(),
        };
        let h2_server_runtime_conf = h2_server_conf.to_runtime_conf(None)?;

        let ssl_connector_conf = match self.ssl_connector_conf.as_ref() {
            Some(conf) => conf,
            None => &SslConnectorConf::default(),
        };
        let ssl_connector_runtime_conf =
            ssl_connector_conf.to_runtime_conf(None)?;

        let core_conf = match &self.core_conf {
            Some(conf) => conf,
            None => &HttpCoreConf::default(),
        };
        let core_runtime_conf = core_conf.to_runtime_conf(None)?;

        let proxy_conf = match &self.proxy_conf {
            Some(conf) => conf,
            None => &HttpProxyConf::default(),
        };
        let proxy_runtime_conf = proxy_conf.to_runtime_conf(None)?;

        let mut ssl_acceptor_builder =
            CustomAcceptorBuilder::new(ssl_acceptor_runtime_conf.clone());
        ssl_acceptor_builder
            .set_dynamic_conf_callback(Box::new(get_ssl_acceptor_conf));
        let ssl_acceptor_ctx = ssl_acceptor_builder
            .build()
            .if_err(ErrorType::ParseError, "while in build ssl acceptor ctx")?;

        // let acceptor_ssl_ctx=
        HttpMainRuntimeConf {
            sites_enabled_path,
            server_conn_pool_capcity,
            client_conn_pool_nums,
            client_conn_pool_capacity,
            client_conn_pool_one_pre_host,
            ssl_connector_runtime_conf,
            h2_server_runtime_conf,
            ssl_acceptor_runtime_conf,
            core_runtime_conf,
            proxy_runtime_conf,
            ssl_acceptor_ctx,
        }
        .valid()
    }
}

#[allow(dead_code)]
pub struct HttpMainRuntimeConf {
    pub sites_enabled_path: String,
    pub server_conn_pool_capcity: usize,
    pub client_conn_pool_nums: usize,
    pub client_conn_pool_capacity: usize,
    pub client_conn_pool_one_pre_host: usize,
    pub ssl_acceptor_runtime_conf: Arc<SslAcceptorRuntimeConf>,
    pub h2_server_runtime_conf: Arc<H2ServerRuntimeConf>,
    pub ssl_connector_runtime_conf: Arc<SslConnectorRuntimeConf>,

    pub core_runtime_conf: Arc<HttpCoreRuntimeConf>,
    pub proxy_runtime_conf: Arc<HttpProxyRuntimeConf>,

    // for ssl acceptor.
    pub ssl_acceptor_ctx: SslContext,
}

#[allow(dead_code)]
impl HttpMainRuntimeConf {
    pub fn valid(self) -> Result<Self> {
        Ok(self)
    }
}

#[allow(dead_code)]
static HTTP_MAIN_RUNTIME_CONF: LazyLock<ArcSwapOption<HttpMainRuntimeConf>> =
    LazyLock::new(|| ArcSwapOption::new(None));

#[allow(dead_code)]
pub fn set_http_main_runtime_conf(conf: HttpMainRuntimeConf) {
    HTTP_MAIN_RUNTIME_CONF.store(Some(Arc::new(conf)));
}

#[allow(dead_code)]
pub fn get_http_main_runtime_conf() -> Arc<HttpMainRuntimeConf> {
    HTTP_MAIN_RUNTIME_CONF.load().clone().unwrap()
}
